package main

import (
	"errors"
	"fmt"
	"gonetdisk/config"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"

	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"github.com/tujiaw/goutil"
)

var (
	// 本地文件浏览目录
	HOME_DIR string

	// 打包下载目录
	ARCHIVE_DIR string

	// 删除文件目录
	TRASH_DIR string
)

// URL路径
const HOME_URL = "/home"

type Nav struct {
	Name   string
	Href   string
	Active bool
}

type RowItem struct {
	Type       string
	Icon       string
	Name       string
	Href       string
	IsDir      bool
	BSize      goutil.ByteSize
	Size       string
	ModTime    string
	PreviewUrl string
}

func InitLog(runDir string) {
	log.SetFormatter(&log.TextFormatter{})
	logdir := path.Join(runDir, "log")
	if !goutil.FileExists(logdir) {
		if err := os.MkdirAll(logdir, os.ModePerm); err != nil {
			panic(err)
		}
	}

	logfile, err := os.OpenFile(path.Join(logdir, "logrus.log"), os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		panic(logfile)
	}

	log.SetOutput(io.MultiWriter([]io.Writer{logfile, os.Stdout}...))
}

func InitDir(runDir, homeDir string) {
	// 检查目录，规范目录格式
	checkDir := func(dir string, isCreate bool) string {
		if !goutil.FileExists(dir) {
			var err error
			if isCreate {
				err = os.MkdirAll(dir, os.ModePerm)
			} else {
				err = fmt.Errorf("dir not exist, path:%v", dir)
			}
			if err != nil {
				panic(err)
			}
		}

		dir, err := filepath.Abs(dir)
		if err != nil {
			panic(err)
		}
		return dir
	}

	runDir = checkDir(runDir, false)
	HOME_DIR = checkDir(homeDir, true)
	ARCHIVE_DIR = checkDir(path.Join(runDir, "__archive__"), true)
	TRASH_DIR = checkDir(path.Join(runDir, "__trash__"), true)

	log.Info("run dir:", runDir)
	log.Info("home dir:", HOME_DIR)
	log.Info("archive dir:", ARCHIVE_DIR)
	log.Info("trash dir:", TRASH_DIR)

	// home目录不能包含运行目录
	if strings.Index(strings.ToLower(runDir), strings.ToLower(HOME_DIR)) >= 0 {
		panic(errors.New("Home dir conflict"))
	}
}

func ReadDirFromUrlPath(urlpath string, query string) ([]RowItem, bool) {
	localDir := path.Join(HOME_DIR, urlpath)
	fullUrl := path.Join(HOME_URL, urlpath)

	var result []RowItem
	var hasMusic bool
	fi, err := ioutil.ReadDir(localDir)
	if err != nil {
		return result, hasMusic
	}

	for i := range fi {
		size := "--"
		bsize := goutil.ByteSize(fi[i].Size())
		href := path.Join(fullUrl, fi[i].Name())

		isDir := fi[i].IsDir()
		if isDir {
			if len(query) > 0 {
				href += "?" + query
			}
		} else {
			size = bsize.Format()
		}

		localPath := path.Join(localDir, fi[i].Name())
		name, icon := config.Instance().GetNameAndIcon(localPath)
		ext := filepath.Ext(localPath)
		result = append(result, RowItem{
			Type:       name,
			Icon:       icon,
			Name:       fi[i].Name(),
			Href:       href,
			IsDir:      isDir,
			BSize:      bsize,
			Size:       size,
			ModTime:    fi[i].ModTime().Format("2006-01-02 15:04:05"),
			PreviewUrl: config.Instance().PreviewUrl(ext, fi[i].Size(), href),
		})
		if !hasMusic && name == "音频" {
			hasMusic = true
		}
	}
	return result, hasMusic
}

func SortFiles(files []RowItem, s string, o string) []RowItem {
	if len(s) == 0 {
		return files
	}

	var isAsc bool
	if o == "asc" {
		isAsc = true
	} else if o == "desc" {
		isAsc = false
	} else {
		return files
	}

	lessString := func(cond bool, left string, right string) bool {
		if cond {
			return left < right
		}
		return left > right
	}

	if s == "name" {
		sort.SliceStable(files, func(i, j int) bool {
			return lessString(isAsc, files[i].Name, files[j].Name)
		})
	} else if s == "time" {
		sort.SliceStable(files, func(i, j int) bool {
			return lessString(isAsc, files[i].ModTime, files[j].ModTime)
		})
	} else if s == "type" {
		sort.SliceStable(files, func(i, j int) bool {
			return lessString(isAsc, files[i].Type, files[j].Type)
		})
	} else if s == "size" {
		sort.SliceStable(files, func(i, j int) bool {
			if isAsc {
				return files[i].BSize < files[j].BSize
			} else {
				return files[i].BSize > files[j].BSize
			}
		})
	}
	return files
}

func GetLocalPath(url string) string {
	if !strings.HasPrefix(url, HOME_URL) {
		return path.Join(HOME_DIR, url)
	}
	return HOME_DIR + url[len(HOME_URL):]
}

func GetRefererPath(c *gin.Context) (string, error) {
	referer := c.Request.Referer()
	urlInfo, err := url.Parse(referer)
	if err == nil && len(referer) == 0 {
		err = errors.New("referrer is empty!")
	}
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		return "", err
	}
	return urlInfo.Path, nil
}

func GetUniquePath(pathstr string) string {
	for {
		if !goutil.FileExists(pathstr) {
			return pathstr
		}

		ext := filepath.Ext(pathstr)
		pathstr = pathstr[:len(pathstr)-len(ext)] + "_bak" + ext
	}
}

func ParseNavList(navpath string, query string) []Nav {
	var result []Nav
	var href string
	nameList := strings.Split(navpath, "/")
	for i, name := range nameList {
		if len(name) > 0 {
			href += "/" + name
			curHref := href
			if len(query) > 0 {
				curHref += "?" + query
			}
			result = append(result, Nav{
				Name:   name,
				Href:   curHref,
				Active: i == len(nameList)-1,
			})
		}
	}
	return result
}
